package com.sort;

/**
 * 计数排序
 * 对于每一个数据,统计整个数组中有多少小于它的数,这样就可以确定它的位置.
 * 比如 : 比它小的有17个,那么它就应该在第18位上
 */
public class CountingSort {
    public static int[] countingSort_1(int[] arr) {
        //找到整个数组中的最大值
        int max = arr[0];
        for (int anArr : arr) {
            if (max < anArr) {
                max = anArr;
            }
        }
        int[] result = new int[arr.length]; //作为排序完成的数组
        int[] temp = new int[max + 1];  //用于保存临时数据
        // 这里将 temp 的下标作为待排数组中的数值,而它对应的元素作为这个数值在数组中的频数
        for (int i = 0; i < arr.length; i++) {
            temp[arr[i]] = temp[arr[i]] + 1;
        }
        // 更新 temp 数组,将它存储的元素表示为:有多少元素小于(或等于)当前下标的值
        for (int i = 1; i <= max; i++) {
            temp[i] += temp[i - 1];
        }
        //将结果输出到 result[] 中
        for (int i = arr.length - 1; i >= 0; i--) {
            result[temp[arr[i]] - 1] = arr[i];
            temp[arr[i]] = temp[arr[i]] - 1;

        }
        return result;
    }

    /*改进1:
     * 观察上述代码,很明显能得到,上面真正能够执行的情况仅能排序的区间是 [ 1 , max ]
     * 局限性很大,因此这里进行改进
     * 改进后的排序算法可以排序的区间是 [ min , max ]
     */
    //传递进来两个参数,说明排序区间为 [ min , max ]
    public static int[] countingSort_2(int[] arr, int min, int max) {
        //已经得到了整个数组中的最大值 max
        int[] result = new int[arr.length]; //作为排序完成的数组
        int[] temp = new int[max - min + 1];  //用于保存临时数据
        // 这里将 temp 的下标作为待排数组中的数值,而它对应的元素作为这个数值在数组中的频数
        for (int i = 0; i < arr.length; i++) {
            temp[arr[i] - min] = temp[arr[i] - min] + 1;
        }
        // 更新 temp 数组,将它存储的元素表示为:有多少元素小于(或等于)当前下标的值
        for (int i = 1; i <= max - min; i++) {
            temp[i] += temp[i - 1];
        }
        //将结果输出到 result[] 中
        for (int i = arr.length - 1; i >= 0; i--) {
            result[temp[arr[i] - min] - 1] = arr[i];
            temp[arr[i] - min] = temp[arr[i] - min] - 1;
        }
        return result;
    }

    /*改进2:
     * 这次改进的不是代码本身,而是操作习惯.
     * 一般而言,当我想调用一个数组对他进行排序的时候,通常不会而且不想知道它的最小值和最大值,
     * 因此找到最小值和最大值的这一步需要我们用代码实现
     *
     * 改进之后,可以直接传入数组进行排序
     */
    public static int[] countingSort_3(int[] arr) {
        int min = arr[0], max = arr[0];
        for (int anArr : arr) {
            if (anArr > max) {
                max = anArr;
            } else if (anArr < min) {
                min = anArr;
            }
        }
        return countingSort_2(arr, min, max);
    }


}
